#include "camera.h"

#include "observer.h"
#include "planar_grid.h"
#include "hemispherical_grid.h"
#include "math/ray.h"
#include "math/vector.h"

namespace Sirat {

namespace Common {

Camera::Camera(const Math::Vector& _observer_position,const Math::Vector& _grid_position,const Math::Vector& _up,const unsigned int _width,const unsigned int _height,Grid_Type _grid_type = Hemispherical) : grid_type(_grid_type) {
	
	Math::Vector y = _up.normalize();
	Math::Vector z = (_grid_position - _observer_position).normalize();
	Math::Vector x = y.cross_product(z);
	
	switch(_grid_type) {
		case Planar:{
			grid = new Planar_Grid(_width,_height,_grid_position,x,y,z);
			break;
		}
		default :{
			grid = new Hemispherical_Grid(_width,_height,_grid_position,x,y,z);
			break;
		}
	}
	observer = new Observer(_observer_position,_up);
}
		
Camera::Camera() { 
	Math::Vector observer_position(0.0,0.0,4.0);
	Math::Vector grid_position(0.0,0.0,2.0);
	Math::Vector up(0.0,1.0,0.0);
	const unsigned int width = 600U;
	const unsigned int height = 600U;
	Grid_Type grid_type = Hemispherical;
	
	Math::Vector y = up.normalize();
	Math::Vector z = (grid_position - observer_position).normalize();
	Math::Vector x = y.cross_product(z);
	
	switch(grid_type) {
		case Planar:{
			grid = new Planar_Grid(width,height,grid_position,x,y,z);
			break;
		}
		default :{
			grid = new Hemispherical_Grid(width,height,grid_position,x,y,z);
			break;
		}
	}
	observer = new Observer(observer_position,up);
}

Camera::Camera(const Camera& _camera) {
	grid = _camera.grid->clone();
	observer = new Observer(*(_camera.observer));
	grid_type = _camera.grid_type;
}

Camera::~Camera() {
	if(grid) delete grid;
	if(observer) delete observer;;
}

void Camera::set_observer(Observer* _observer) {
	observer = _observer;
}

void Camera::set_grid(Grid* _grid) {
	grid = _grid;
}

void Camera::set_grid_type(Grid_Type _grid_type) {
	Math::Vector y = observer->get_up().normalize();
	Math::Vector z = (grid->get_position() - observer->get_position()).normalize();
	Math::Vector x = y.cross_product(z);
	
	switch(_grid_type) {
		case Planar:{
			grid = new Planar_Grid(grid->get_width(),grid->get_height(),grid->get_position(),x,y,z);
			break;
		}
		default:{
			grid = new Hemispherical_Grid(grid->get_width(),grid->get_height(),grid->get_position(),x,y,z);
			break;
		}
	}
}

Observer* Camera::get_observer() {
	return observer;
}

Grid* Camera::get_grid() {
	return grid;
}

Grid_Type Camera::get_grid_type() const {
	return grid_type;
}

void Camera::look_at(const Math::Vector& _observer_position,const Math::Vector& _grid_position,const Math::Vector& _up) {
	Math::Vector y = _up.normalize();
	Math::Vector z = (_grid_position - _observer_position).normalize();
	Math::Vector x = y.cross_product(z);
	
	grid->set_x(x);
	grid->set_y(y);
	grid->set_z(z);
	observer->set_position(_observer_position);
	observer->set_up(_up);
}

Math::Ray Camera::generate_ray(const unsigned int _x,const unsigned int _y) {
	Math::Vector direction = grid->get_position() - observer->get_position() + (grid->projection_transformation(_x,_y));
	return Math::Ray(observer->get_position(),direction.normalize());
}

Camera& Camera::operator=(const Camera& _camera) {
	grid = _camera.grid->clone();
	observer = new Observer(*(_camera.observer));;
	grid_type = _camera.grid_type;
	return *this;
}

}

}
